package org.apache.osgimaker.editors.content;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.osgimaker.editors.model.Bundle;
import org.apache.osgimaker.editors.model.BundleListEditModel;
import org.apache.osgimaker.utils.CachingContentProposalProvider;
import org.apache.osgimaker.utils.JavaContentProposalLabelProvider;
import org.apache.osgimaker.utils.JavaTypeContentProposal;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.IPartSelectionListener;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.ide.ResourceUtil;
import org.osgi.framework.BundleActivator;


public class GeneralInfoPart extends SectionPart implements PropertyChangeListener,IPartSelectionListener{

	private BundleListEditModel model;
	private IManagedForm managedForm;
	
	private Text txtSymbolicName;
	private Text txtVersion;
	private Text txtActivator;
	private String modifyBundleId;
	
	public static final char[] AUTO_ACTIVATION_CLASSNAME = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._".toCharArray();
	
	public GeneralInfoPart(Composite parent, FormToolkit toolkit, int style) {
		super(parent, toolkit, style);
		createSection(getSection(), toolkit);
	}
	
	private void createSection(Section section, FormToolkit toolkit) {
		section.setText("Basic Information");

		KeyStroke assistKeyStroke = null;
		try {
			assistKeyStroke = KeyStroke.getInstance("Ctrl+Space");
		} catch (ParseException x) {
			// Ignore
		}
		FieldDecoration contentAssistDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
		FieldDecoration infoDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION);

		Composite composite = toolkit.createComposite(section);
		section.setClient(composite);

		toolkit.createLabel(composite, "Symbolic Name:");
		txtSymbolicName = toolkit.createText(composite, "", SWT.BORDER);

		toolkit.createLabel(composite, "Version:");
		txtVersion = toolkit.createText(composite, "", SWT.BORDER);

		Hyperlink linkActivator = toolkit.createHyperlink(composite, "Activator:", SWT.NONE);
		txtActivator = toolkit.createText(composite, "", SWT.BORDER);

		// Content Proposal for the Activator field
		ContentProposalAdapter activatorProposalAdapter = null;

		ActivatorClassProposalProvider proposalProvider = new ActivatorClassProposalProvider();
		activatorProposalAdapter = new ContentProposalAdapter(txtActivator, new TextContentAdapter(), proposalProvider, assistKeyStroke, AUTO_ACTIVATION_CLASSNAME);
		activatorProposalAdapter.addContentProposalListener(proposalProvider);
		activatorProposalAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
		activatorProposalAdapter.setLabelProvider(new JavaContentProposalLabelProvider());
		activatorProposalAdapter.setAutoActivationDelay(1000);

		ControlDecoration decorActivator = new ControlDecoration(txtActivator, SWT.LEFT | SWT.TOP, composite);
		decorActivator.setImage(contentAssistDecoration.getImage());
		decorActivator.setDescriptionText(MessageFormat.format("Content Assist is available. Press {0} or start typing to activate", assistKeyStroke.format()));
		decorActivator.setShowOnlyOnFocus(true);
		decorActivator.setShowHover(true);



		// Layout
		GridLayout layout = new GridLayout(2, false);

		composite.setLayout(layout);

		GridData gd;
		gd = new GridData(SWT.FILL, SWT.TOP, true, false);
		gd.horizontalIndent = 5;
		txtSymbolicName.setLayoutData(gd);

		gd = new GridData(SWT.FILL, SWT.TOP, true, false);
		gd.horizontalIndent = 5;
		txtVersion.setLayoutData(gd);

		gd = new GridData(SWT.FILL, SWT.TOP, true, false);
		gd.horizontalIndent = 5;
		txtActivator.setLayoutData(gd);
		
		ModifyListener modifyListener = new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				markDirty();
			}
		};
		
		this.txtSymbolicName.addModifyListener(modifyListener);
		this.txtVersion.addModifyListener(modifyListener);
		this.txtActivator.addModifyListener(modifyListener);
	}
	
	@Override
	public void propertyChange(PropertyChangeEvent arg0) {
		
	}

	IJavaProject getJavaProject() {
		IFormPage formPage = (IFormPage) getManagedForm().getContainer();
		IFile file = ResourceUtil.getFile(formPage.getEditorInput());
		return JavaCore.create(file.getProject());
	}

	
	private class ActivatorClassProposalProvider extends CachingContentProposalProvider {
        @Override
        protected List<IContentProposal> doGenerateProposals(String contents, int position) {
            final String prefix = contents.substring(0, position);
            final IJavaProject javaProject = getJavaProject();
            try {
                final List<IContentProposal> result = new ArrayList<IContentProposal>();

                final IRunnableWithProgress runnable = new IRunnableWithProgress() {
                    public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                        try {
                            IType activatorType = javaProject.findType(BundleActivator.class.getName());
                            if (activatorType != null) {
                                ITypeHierarchy hierarchy = activatorType.newTypeHierarchy(javaProject, monitor);
                                for (IType subType : hierarchy.getAllSubtypes(activatorType)) {
                                    if (!Flags.isAbstract(subType.getFlags()) && subType.getElementName().toLowerCase().contains(prefix.toLowerCase())) {
                                        result.add(new JavaTypeContentProposal(subType));
                                    }
                                }
                            }
                        } catch (JavaModelException e) {
                            throw new InvocationTargetException(e);
                        }
                    }
                };

                IWorkbenchWindow window = ((IFormPage) getManagedForm().getContainer()).getEditorSite().getWorkbenchWindow();
                window.run(false, false, runnable);
                return result;
            } catch (InvocationTargetException e) {
                return Collections.emptyList();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return Collections.emptyList();
            }
        }

		@Override
		protected boolean match(String contents, int position, IContentProposal proposal) {
			String prefix = contents.substring(0, position);
			return ((JavaTypeContentProposal) proposal).getTypeName().toLowerCase().startsWith(prefix.toLowerCase());
		}
	}

	@Override
	public void commit(boolean onSave) {
		super.commit(onSave);
		//model.setBundleList(bundles);
		Bundle bundle = new Bundle();
		bundle.setId(modifyBundleId);
		bundle.setSymbolicName(this.txtSymbolicName.getText().trim());
		bundle.setVersion(this.txtVersion.getText().trim());
		bundle.setActivator(this.txtActivator.getText().trim());
		model.updateBundleGeneralInfo(bundle.getId(), bundle);
		managedForm.dirtyStateChanged();
	}

	@Override
	public void refresh() {
		/*List<String> tmp = model.getBundleList();
		if(tmp != null)
			bundles = new ArrayList<String>(tmp);
		else
			bundles = new ArrayList<String>();
		viewer.setInput(bundles);*/
		super.refresh();
	}
	
	@Override
	public void initialize(IManagedForm form) {
		super.initialize(form);
		this.managedForm = form;
		model = (BundleListEditModel) form.getInput();
	}
	
	@Override
	public void dispose() {
		super.dispose();
		if(this.model != null)
			this.model.removePropertyChangeListener(this);
	}

	@Override
	public void selectionChanged(IFormPart part, ISelection selection) {
		Object[] tmp = ((IStructuredSelection) selection).toArray();
		for(int i=0;i<tmp.length;i++){
			String id = tmp[i].toString();
			Bundle bundle = model.getBundleById(id);
			txtSymbolicName.setText(bundle.getSymbolicName()==null?"":bundle.getSymbolicName());
			txtVersion.setText(bundle.getVersion()==null?"":bundle.getVersion());
			txtActivator.setText(bundle.getActivator()==null?"":bundle.getActivator());
			modifyBundleId = id;
			break;
		}
		
	}
}
